Generated code - Databinding with ASP.NET 2.0, Adapter
Preface
In ASP.NET 2.0 things have changed drastically compared to ASP.NET 1.x. In ASP.NET 2.0, databinding is now fully 2-way, and can be setup declaratively, which
means you can setup databinding completely in HTML, without the necessity of code in the code-behind file. This section is about databinding with a set of
data using a
DataSource control. .NET 2.0 ships with a couple of these controls, like SqlDataSource control and for example the ObjectDataSource control.
LLBLGen Pro datasource controls
LLBLGen Pro ships with its own DataSource controls:
LLBLGenProDataSource for selfservicing and LLBLGenProDataSource2 for adapter. These also are located in the SD.LLBLGen.Pro.ORMSupportClasses dll for .NET 2.0
and you have to add them to the toolbox first. This can be done manually by right-clicking the toolbox when a webform is open in the editor (HTML or design view)
and then by selecting 'Choose items...' which allows you to browse to the ORMSupportClasses dll for .NET 2.0. It's key you add the ORMSupportClasses dll version
you're also using in your ASP.NET project, as VS.NET can load only one version of a given assembly and this could lead to type clashes in the webform designer. This
is a problem with VS.NET 2005 and which is almost entirely solved by installing VS.NET 2005 SP1.
Getting started with the LLBLGenProDataSource2 control
Dragging the datasource control of choice onto a form in design mode will open show you the smart tag to configure the datasource. It's
key to first reference
the correct ORMSupportClasses library in your web project. The LLBLGenProDataSource2 control can be used for an EntityCollection, TypedList or TypedView.
It's recommended you use the smart-tag to setup the LLBLGenProDataSource2 control, however you can also set it up in the HTML. To get the idea what you've to
setup, it's easier to first setup a couple of LLBLGenProDataSource2 controls through its designer by using the smart-tag and then when you see what the HTML
looks like, you can from then on reproduce it in HTML. To learn more about the specific properties of the LLBLGenProDataSource2 control, please consult the
LLBLGen Pro reference manual for the LLBLGenProDataSource2 control, which is in the ORMSupportClasses assembly.
The LLBLGenProDataSource2 control accepts a type specification which is used for the particular container type: so
an entity factory for the EntityCollection, the type of a typedlist or the type of a typedview. The container type, thus what kind of object is contained
in the LLBLGenProDataSource2 control, is specified using the property
DataContainerType, accessable in the designer for the LLBLGenProDataSource2 control
and also in the property grid of VS.NET. The contained object itself is exposed through the property
belonging to the value of this property: so if the DataContainerType is set to EntityCollection, an EntityCollection is inside the datasourcecontrol and the
EntityCollection property is valid, though when DataContainerType is set to
TypedList, a TypedList object is contained in the LLBLGenProDataSource2
control, and if you then read the property
EntityCollection, for example in the code behind, it will throw an InvalidOperationException, as it's not
defined, you should have read the TypedList property instead.
The GroupBy property is suppored in TypedList/TypedView scenario's while Prefetch path objects are supported in EntityCollection scenarios.
Caching of data.
The LLBLGenProDataSource2 control contains a data containing object, be it an entity collection, TypedList or TypedView. This data is cached in-between postbacks,
until the data has to be refreshed. You can also disable this caching by setting it to None. If caching is enabled (default), the place where this data is cached is either in the viewstate, ASP.NET cache or the session. Which one, Viewstate, ASP.NET cache or session,
depends on the value of the LLBLGenProDataSource2 control's property
CacheLocation. This property is of type
DataSourceCacheLocation which
is an enum and which can be 'ViewState', 'ASPNetCache', 'Session' or 'None'. Viewstate is the default. If the CacheLocation is set to
Session, the data is stored in the session object with a key with the following name:
__LLBLGENPRODATASOURCEDATA_controlUniqueID_BindingContainerName
ControlUniqueID is the UniqueID of the DataSource control on the page.
BindingContainerName is the name of the container the control is located in. This key is stored in the control state and is always preserved.
If the
CacheLocation is set to ASPNetCache, the data is stored in the ASP.NET Cache using the following key:
__LLBLGENPRODATASOURCEDATA_Guid
where the
Guid is a new Guid per LLBLGenProDataSource2 control instance. You can control the ASP.NET Cache duration as well. Use for that the LLBLGenProDataSource control property
ASPNetCacheDuration which indicates in minutes the time the cached data should stay in the cache. Recommended is to keep it as long as a session duration so users won't run into missing data if there's some delay between page render and page postback. Default is 20 minutes.
Disabling caching, by setting it to DataSourceCacheLocation.None, has the effect that if the LLBLGenProDataSource2 control is forced to fetch data by a call to ExecuteSelect, it always will refetch the data from the database. This can lead to different data in the page, so you should be aware of this when using the None setting. As no data is cached, a bound control should use the data in a read-only fashion.
When the CacheLocation is set to None, the LLBLGenProDataSource2 control will still cache its state somewhere, and it will use the ViewState for that.
Setting the data container manually.
The controls offer properties (EntityCollection, TypedList, TypedView) to set the contained object to an external object, for example an EntityCollection
object you've fetched in a method in your business logic tier. This also offers the ability to for example bind
myCustomer.Orders to grids through the datasource controls by simply setting the property EntityCollection in the code behind file of the webform. You can do
this too with TypedLists by setting the TypedList property and with TypedViews by setting the TypedView property. Be sure to first set the
DataContainerType
property to the right container type, i.e. EntityCollection, TypedList or TypedView.
Two way databinding.
Two way databinding can be done with the DataContainerType set to
EntityCollection. This means the LLBLGenProDataSource2 control manages not only the
binding of data to a control but also the manipulation of data through the bound control (e.g. a GridView) of the data in the contained EntityCollection. As
TypedList and TypedView classes are read-only by definition, you can't manipulate data inside these classes through the LLBLGenProDataSource2 control. How the
LLBLGenProDataSource2 control fetches data (automatically or through code placed inside an eventhandler) as well as how it saves data (automatically or through
event handlers) is discussed in the next section.
LivePersistence and events
A property of the LLBLGenProDataSource2 control, called
LivePersistence, which can be true or false, is used to signal the LLBLGenProDataSource2 control
to perform the select, insert, update and delete actions directly on the database (true) or modify the entity collection and add the actions to a UnitOfWork object
(false), and to use external methods to fetch data for typedlist fetches, typedview fetches and EntityCollection fetches.
If LivePersistence is set to false, fetching data and saving data isn't performed by the LLBLGenProDataSource2 control but an event is raised instead
(see below,
Intercepting activity)
which passes an event arguments object which contains the parameters for the fetch or save and allows own code to perform the fetch or save. In any case,
the changed event is raised so the bound control(s) can refetch the data from the LLBLGenProDataSource2 control. LivePersistence causes 3 events to be raised:
- PerformSelect. This event is raised when the LLBLGenProDataSource2 control needs to retrieve data from the database. Use the passed-in PerformSelectEventArgs2 object to perform the Fetch action. You should fetch the data into the appropriate object inside the PerformSelectEventArgs2 object, for example the ContainedCollection using the parameters available in the PerformSelectEventArgs2 object.
- PerformGetDbCount. This event is raised when the LLBLGenProDataSource2 control needs to retrieve the number of items in the complete
resultset. This event is raised when server side paging is enabled and the total number of items in the resultset is required by the bound control(s). Your handler should fetch the count of the set to fetch by using the passed in PerformGetDbCountEventArgs2 object which contains all information necessary for the retrieval of the count value. Set the DbCount property of the PerformGetDbCountEventArgs2 object to the value read from the database. Be sure to pass to the DataAccessAdapter.GetDbCount call the filter, prefetch path, sort expression and other objects available to you via the passed-in PerformGetDbCountEventArgs2 object.
- PerformWork. This event is raised when ExecuteInsert/Update/Delete is called on the LLBLGenProDataSource2 control by a bound control.
Typically this is done after an entity is edited in a GridView or FormView control for example, or a new entity is added through a bound control.
The work is tracked in a UnitOfWork2 object which is available to you in the passed in PerformWorkEventArgs2 object.
When a refetch of the data has taken place, the UnitOfWork2 object contained in the control is cleared. This means that any update/insert/delete work pending has
to be completed by that point.
Please examine the events and special event argument classes in the LLBLGen Pro
Reference manual.
Refetch
To ensure fresh data from the database. is retrieved, a flag on the LLBLGenProDataSourceControl2 called
Refetch can be set to true, so the DefaultView
will refetch the data, even if for example the page number is the same. This can be necessary if the code-behind code decides the data represented by the
LLBLGenProDataSource2 control is invalidated and has to be refetched from the database.
Using the LLBLGenProDataSource2 control.
Binding a LLBLGenProDataSource2 control is simple, just add the ID as DataSourceID in the bound control's HTML, or select the LLBLGenProDataSource2 control from
the dropdownbox for available datasources in the bound control's smart-tag. The designer of the LLBLGenProDataSource2 control will allow the user to select
the DataAccessAdapter, the DataContainerType and the type of object needed for the container (factory, typedlist or typedview type).
By default no filter is set, no groupby, no prefetch path, no sorting. The EntityView2 object returned by the contained EntityCollection's DefaultView method
is returned from the EntityCollection contained in the LLBLGenProDataSource2 control.
To set a filter, prefetch path or sort expression, a code behind page is required to set these parameters. This is due to the requirement that these are
compile time checked. You can however also produce filters at runtime by using the ASP.NET 2.0 parameter binding feature. This allows you to setup a binding between a
control (or cookie, form etc.) which produces a value and the datasource control so the value produced by the other control is used for filtering.
See the example below which uses two drop down boxes to create a filter at runtime for fetching data. Paging is supported as well: if you want server-side paging,
define the paging parameters on the LLBLGenProDataSource2 in the VS.NET property grid. If you want paging inside your bound control, define the
paging parameters in the bound control, for example the GridView. To be able to work with paging, you of course have to enable paging on the bound control.
Intercepting activity
As described above, the LLBLGenProDataSource2 control has various events to bind to to intercept what's going on inside the LLBLGenProDataSource2 control at runtime.
For the
PerformSelect event, the event arguments contain what the container type is, the container, and all the parameters
to perform the fetch. Not all parameters of the argument are valid in every situation, for example a typedlist fetch requires a field-set but doesn't need an entity
collection. The event arguments for the event
PerformGetDbCount contain an integer called
DbCount which should contain the determined count after the
event has been handled. The event arguments for the
PerformWork event contain a UnitOfWork2 object with the work to be performed. Typically this is
a single entity, as every time the bound control updates or deletes an entity, the PerformWork event is raised, due to the postback nature of ASP.NET.
The PerformWork event in an AJAX environment
When you use a grid like the DevExpress ASPxGrid for .NET 2.0, you can have all edit activities on the client side, using AJAX communication with the server. When
you've setup the grid to be used on the client side, and the LLBLGenProDataSource2 control has LivePersistence set to false, the changes made to the data on the
client will be performed in one go when the page gets a post-back. In this scenario, it's more efficient not to bind to PerformWork, but to place a button on the
form which simply performs the 'save', e.g. it says "Save changes". By not binding to the PerformWork event, all ExecuteInsert/Update/Delete actions will take place
on the data, but aren't propagated to the database just yet, because LivePersistence is set to false. In the button handler of your save button, you then retrieve
the UnitOfWork2 object from the LLBLGenProDataSource2 control which contains all changes made and you can commit the changes in one transaction.
Filtering on the fly
The LLBLGenProDataSource2 control supports filtering on the fly based on parameters specified which can retrieve values from other controls, forms,
cookies or other objects supported by the SelectParameters feature of ASP.NET 2.0 which is also available in a LLBLGenProDataSource2 control via its
SelectParameters property. The SelectParameters property follows the same specification as the ObjectDataSource and are specified declaratively using the
<SelectParameters> tag inside a LLBLGenProDataSource2 control declaration. You can also use the VS.NET designer for setting up the SelectParameters, to do that
simply click the [...] button next to SelectParameters in the Property grid of VS.NET. Be sure that the parameter name has the
same name as a field in the
entity, typed list row or typed view row. You can't use SelectParameters to build a filter on non-entity fields as it builds a filter for the next fetch.
The following example shows this in action.
Trapping invalid input values
As the LLBLGenProDataSource2 control is the receiver of the data filled into a form, grid or other bound control, it can be these values are invalid, for example they don't match the type of the entity field or they are too big in size. By default, the LLBLGenProDataSource2 control will ignore these values and won't throw an exception. To make the control throw an exception after all values have been evaluated, you can set the LLBLGenProDataSource2 property
ThrowExceptionOnIllegalFieldInput to true, which signals the control to throw an exception if one or
more fields received an illegal value which wasn't convertable to the type of the field in an update/insert scenario. If set to true all illegal values are collected and added to one single
ORMValueTypeMismatchException so your code will receive just one exception to handle them all. If set to false, the illegal values are ignored and the fields
don't get set to a new value, which was the default behavior in v2.0.
Usage examples
Below are two examples: one example using LivePersistence set to true and one using LivePersistence set to false. They're basicly the same form. For VB.NET users: the HTML is for C#, you've to change the first line of the given HTML snippets into the following line to use it with VB.NET:
<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>
Example using LivePersistence
This example contains a
form, which filters a list of Order entities, provided by the LLBLGenProDataSource2 control
orderDS, on the OrderEntity field
ShippingCountry, provided by a static drop down box, and a drop down box with all customers from Northwind, provided by LLBLGenProDataSource2 control
customerDS. Using parameter binding the two dropdown boxes produce filter information at runtime to produce the proper order list. No code required,
it's completely declarative HTML.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="SD.LLBLGen.Pro.ORMSupportClasses"
Namespace="SD.LLBLGen.Pro.ORMSupportClasses" TagPrefix="llblgenpro" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
All customers:<br />
Customers:
<asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="customerDS"
DataTextField="CompanyName" DataValueField="CustomerId" AutoPostBack="True">
</asp:DropDownList><br />
ShipCountry:
<asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="True">
<asp:ListItem>Spain</asp:ListItem>
<asp:ListItem>Germany</asp:ListItem>
</asp:DropDownList><br />
<llblgenpro:llblgenprodatasource2 id="customerDS" runat="server"
cachelocation="Session" datacontainertype="EntityCollection" enablepaging="True"
AdapterTypeName="NW20.DatabaseSpecific.DataAccessAdapter, NW20DBSpecific"
EntityFactoryTypeName="NW20.FactoryClasses.CustomerEntityFactory, NW20">
</llblgenpro:llblgenprodatasource2>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="orderDS"
DataKeyNames="OrderId" AllowPaging="True" PageSize="5">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:BoundField DataField="ShipAddress" HeaderText="ShipAddress" SortExpression="ShipAddress" />
<asp:BoundField DataField="ShipName" HeaderText="ShipName" SortExpression="ShipName" />
<asp:BoundField DataField="ShipCountry" HeaderText="ShipCountry" SortExpression="ShipCountry" />
<asp:BoundField DataField="CustomerId" HeaderText="CustomerId" SortExpression="CustomerId" />
<asp:BoundField DataField="ShipRegion" HeaderText="ShipRegion" SortExpression="ShipRegion" />
<asp:BoundField DataField="ShipCity" HeaderText="ShipCity" SortExpression="ShipCity" />
<asp:BoundField DataField="OrderId" HeaderText="OrderId" SortExpression="OrderId" />
<asp:BoundField DataField="ShipPostalCode" HeaderText="ShipPostalCode" SortExpression="ShipPostalCode" />
</Columns>
</asp:GridView>
<llblgenpro:llblgenprodatasource2 id="orderDS" runat="server" cachelocation="Session"
datacontainertype="EntityCollection" enablepaging="True"
AdapterTypeName="NW20.DatabaseSpecific.DataAccessAdapter, NW20DBSpecific"
EntityFactoryTypeName="NW20.FactoryClasses.OrderEntityFactory, NW20">
<SelectParameters>
<asp:ControlParameter ControlID="DropDownList1" Name="CustomerId"
PropertyName="SelectedValue" Type="String" />
<asp:ControlParameter ControlID="DropDownList2" Name="ShipCountry"
PropertyName="SelectedValue" Type="String" />
</SelectParameters>
</llblgenpro:llblgenprodatasource2>
</form>
</body>
</html>
Example using Perform Event handlers
The following example is the same form as in the previous example, with the same functionality, however now it uses
LivePersistence set to false, which means we've to perform the persistence logic ourselves by writing event handlers. The HTML is shown first, which is roughly the same as the previous example's HTML except it defines bindings to EventHandlers for PerformGetDbCount, PerformSelect and PerformWork. After that the code behind in VB.NET and C# is shown.
HTML page
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="SD.LLBLGen.Pro.ORMSupportClasses"
Namespace="SD.LLBLGen.Pro.ORMSupportClasses" TagPrefix="llblgenpro" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
All customers:<br />
Customers:
<asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="customerDS"
DataTextField="CompanyName" DataValueField="CustomerId" AutoPostBack="True">
</asp:DropDownList><br />
ShipCountry:
<asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="True">
<asp:ListItem>Spain</asp:ListItem>
<asp:ListItem>Germany</asp:ListItem>
</asp:DropDownList><br />
<llblgenpro:llblgenprodatasource2 id="customerDS" runat="server"
cachelocation="Session" datacontainertype="EntityCollection" enablepaging="True"
AdapterTypeName="NW20.DatabaseSpecific.DataAccessAdapter, NW20DBSpecific"
EntityFactoryTypeName="NW20.FactoryClasses.CustomerEntityFactory, NW20"
LivePersistence="False" OnPerformSelect="customerDS_PerformSelect">
</llblgenpro:llblgenprodatasource2>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="orderDS"
DataKeyNames="OrderId" AllowPaging="True" PageSize="5">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:BoundField DataField="ShipAddress" HeaderText="ShipAddress" SortExpression="ShipAddress" />
<asp:BoundField DataField="ShipName" HeaderText="ShipName" SortExpression="ShipName" />
<asp:BoundField DataField="ShipCountry" HeaderText="ShipCountry" SortExpression="ShipCountry" />
<asp:BoundField DataField="CustomerId" HeaderText="CustomerId" SortExpression="CustomerId" />
<asp:BoundField DataField="ShipRegion" HeaderText="ShipRegion" SortExpression="ShipRegion" />
<asp:BoundField DataField="ShipCity" HeaderText="ShipCity" SortExpression="ShipCity" />
<asp:BoundField DataField="OrderId" HeaderText="OrderId" SortExpression="OrderId" />
<asp:BoundField DataField="ShipPostalCode" HeaderText="ShipPostalCode" SortExpression="ShipPostalCode" />
</Columns>
</asp:GridView>
<llblgenpro:llblgenprodatasource2 id="orderDS" runat="server" cachelocation="Session"
datacontainertype="EntityCollection" enablepaging="True"
AdapterTypeName="NW20.DatabaseSpecific.DataAccessAdapter, NW20DBSpecific"
EntityFactoryTypeName="NW20.FactoryClasses.OrderEntityFactory, NW20" LivePersistence="False"
OnPerformGetDbCount="orderDS_PerformGetDbCount" OnPerformSelect="orderDS_PerformSelect"
OnPerformWork="orderDS_PerformWork">
<SelectParameters>
<asp:ControlParameter ControlID="DropDownList1" Name="CustomerId"
PropertyName="SelectedValue" Type="String" />
<asp:ControlParameter ControlID="DropDownList2" Name="ShipCountry"
PropertyName="SelectedValue" Type="String" />
</SelectParameters>
</llblgenpro:llblgenprodatasource2>
</form>
</body>
</html>
Code behind
// C#
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using SD.LLBLGen.Pro.ORMSupportClasses;
using NW20.DatabaseSpecific;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void customerDS_PerformSelect(object sender, PerformSelectEventArgs2 e)
{
// fetch all customers using the information passed in via the
// PerformSelectEventArgs2 object. This select doesn't have to perform any paging, as the
// data is for a combo box.
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
adapter.FetchEntityCollection(e.ContainedCollection, e.Filter,
e.MaxNumberOfItemsToReturn, e.Sorter, e.PrefetchPath);
}
}
protected void orderDS_PerformGetDbCount(object sender, PerformGetDbCountEventArgs2 e)
{
// get the total number of orders which match the filter passed in via the
// PerformGetDbCountEventArgs2.
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
e.DbCount = adapter.GetDbCount(e.ContainedCollection, e.Filter);
}
}
protected void orderDS_PerformSelect(object sender, PerformSelectEventArgs2 e)
{
// fetch all orders which are in the selected page using the filter passed in
// via the PerformSelectEventArgs2 object.
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
adapter.FetchEntityCollection(e.ContainedCollection, e.Filter,
e.MaxNumberOfItemsToReturn, e.Sorter, e.PrefetchPath,
e.PageNumber, e.PageSize);
}
}
protected void orderDS_PerformWork(object sender, PerformWorkEventArgs2 e)
{
// Perform the work passed in via the PerformWorkEventArgs2 object.
// Start a new transaction with the passed in unit of work.
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
// pass the adapter to the Commit routine and tell it to autocommit
// when the work is done.
e.Uow.Commit(adapter, true);
}
}
}
' VB.NET
Imports SD.LLBLGen.Pro.ORMSupportClasses
Imports NW20.DatabaseSpecific
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub customerDS_PerformSelect(ByVal sender As Object, ByVal e As PerformSelectEventArgs2) _
Handles customerDS.PerformSelect
' fetch all customers using the information passed in via the
' PerformSelectEventArgs2 object. This select doesn't have to perform any paging, as the
' data is for a combo box.
Using adapter As New DataAccessAdapter()
adapter.FetchEntityCollection(e.ContainedCollection, e.Filter, _
e.MaxNumberOfItemsToReturn, e.Sorter, e.PrefetchPath)
End Using
End Sub
Protected Sub orderDS_PerformGetDbCount(ByVal sender As Object, ByVal e As PerformGetDbCountEventArgs2) _
Handles orderDS.PerformGetDbCount
' get the total number of orders which match the filter passed in via the
' PerformGetDbCountEventArgs2.
Using adapter As New DataAccessAdapter()
e.DbCount = adapter.GetDbCount(e.ContainedCollection, e.Filter)
End Using
End Sub
Protected Sub orderDS_PerformSelect(ByVal sender As Object, ByVal e As PerformSelectEventArgs2) _
Handles orderDS.PerformSelect
' fetch all orders which are in the selected page using the filter passed in
' via the PerformSelectEventArgs2 object.
Using adapter As New DataAccessAdapter()
adapter.FetchEntityCollection(e.ContainedCollection, e.Filter, _
e.MaxNumberOfItemsToReturn, e.Sorter, e.PrefetchPath, _
e.PageNumber, e.PageSize)
End Using
End Sub
Protected Sub orderDS_PerformWork(ByVal sender As Object, ByVal e As PerformWorkEventArgs2) _
Handles orderDS.PerformWork
' Perform the work passed in via the PerformWorkEventArgs2 object.
' Start a new transaction with the passed in unit of work.
Using adapter As New DataAccessAdapter()
' pass the adapter to the Commit routine and tell it to autocommit
' when the work is done.
e.Uow.Commit(adapter, True)
End Using
End Sub
End Class
Setting values for insert/update using bound parameters
One new feature of ASP.NET 2.0 is bound parameters, where you can define parameters which retrieve the values from other controls, cookies, query string etc.
One example is given above, using filtering based on the SelectParameters. The LLBLGenProDataSource2 control supports also InsertParameters and UpdateParameters.
You can define these parameters for insert (saving a new entity) and update
(saving a changed entity) resp. the same way as you do with SelectParameters. This way you can for example set the 'EmployeeId' on a new order entity where you
retrieve the EmployeeId from a dropdown control. The value retrieved through a parameter overrules a set value through the bound control. If no value is
passed in by the bound control and it's available through the InsertParameters (when inserting) or UpdateParameters (when updating), the value in the
Insert/UpdateParameters collection is chosen.
Converting empty string values ("") to NULL values for inserts/updates
In a web-application, form values which are empty are represented as an empty string (""). When an entity is edited through a form, it can be some textboxes
or other controls bound to fields of the entity are left empty / point to an empty value: "". The LLBLGenProDataSource2 control will convert "" into NULL
for all fields which .NET type isn't the string type. If the field
is the string type, this can give a problem: what if the empty string is a valid value?
To tell the LLBLGenProDataSource2 control that a field should get the empty string as a valid value instead of NULL, you've to pass a List(of String) object
with all the fieldnames of the fields which should accept "" as the valid value to the property
FieldNamesKeepEmptyStringAsValue of the
LLBLGenProDataSource2 control. You should do this in the code behind of your webform.
Example
// C#
// in your Page Load handler routine
if( !Page.IsPostBack )
{
List<string> fieldsWhichShouldKeepEmptyString = new List<string>();
fieldsWhichShouldKeepEmptyString.Add( "ShipAddress" );
_ordersDS.FieldNamesKeepEmptyStringAsValue = fieldsWhichShouldKeepEmptyString;
}
' VB.NET
' in your Page Load handler routine
If Not Page.IsPostBack Then
Dim fieldsWhichShouldKeepEmptyString As New List(Of String)()
fieldsWhichShouldKeepEmptyString.Add( "ShipAddress" )
_ordersDS.FieldNamesKeepEmptyStringAsValue = fieldsWhichShouldKeepEmptyString
End If
This example tells the LLBLGenProDataSource2 control called '_ordersDS' that the field
ShipAddress should get the value "" instead of NULL
if the form value is "" for that field.
Normally you don't need to set the property FieldNamesKeepEmptyStringAsValue, if "" is not used for string values and NULL is acceptable instead.
It can be that the list of names which keep the empty string as the value is actually the complete set of fields of the entity. In that case, you can set the property
AllFieldsKeepEmptyStringAsValue to true, which makes the LLBLGenProDataSource2 control to simply not convert empty strings to NULL values for entity fields. Setting this property to true will make LLBLGenProDataSource2 control to ignore FieldNamesKeepEmptyStringAsValue.
The SortingMode property
The LLBLGenProDataSource2 control is capable to apply sorting when data has to be fetched, or for example when you've clicked a column header in a
bound GridView control. By default, the LLBLGenProDataSource2 control sorts on the server-side, by producing a SortExpression which is then used by the fetch
logic. Which SortExpression is used depends on the value of the property
SorterToUse of the LLBLGenProDataSource2 control and the columns specified
by the bound control (e.g. clicked column header). It's possible to tell the LLBLGenProDataSource2 control to sort on the
client-side instead. Do
this by setting the LLBLGenProDataSource2 property
SortingMode.
By default it's set to ServerSide. If you set it to ClientSide, sorting is applied after the fetch, by sorting the DefaultView object of the
datasourcecontrol.
Server-side sorting only uses EntityField2 objects, so if the entity has a field which isn't a field mapped onto a table/view field, it's ignored in the
server-side sorting actions because it's not part of the query send to the database. This is also true for fields mapped onto related fields. In these
situations, use client-side sorting.
Be aware that if the CacheLocation is set to None, the LLBLGenProDataSource2 control always has to fetch the data from the database again, as it can't sort cached data in-memory. If you want to avoid the roundtrip to the database, set the CacheLocation to another value than None.